using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;
using GoodStuff;

namespace SampleProcessors
{
    public class Blob
    {
        int _area;
        public int Area { get { return _area; } }

        List<Rectangle> _spans;

        public Blob()
        {
            _spans = new List<Rectangle>();
        }
        public List<Rectangle> Spans
        {
            get { return _spans; }
        }

        public void AddSpan(Rectangle r)
        {
            _spans.Add(r);
            _area += r.Width;
        }
    }

    public class BlobFinder : WorkerThread, ISampleProcessor
    {
        ThreadSafeQueue<Sample> _frameQueue;

        public BlobFinder()
        {
            _frameQueue = new ThreadSafeQueue<Sample>(10);
        }

        #region IFrameProcessor Members

        public void ProcessFrame(object sender, SampleAcquiredEventArgs args)
        {
            _frameQueue.Enqueue(args.Frame);
        }

        public event FrameCompleteEventHandler FrameComplete;

        #endregion

        #region WorkerThread members

        protected override void ThreadProc()
        {
            while (KeepGoing(100))                                                   // loop until quit signal
            {
                _frameQueue.Wait(100);                                               // wait for a frame to be queued

                while (_frameQueue.Count > 0)                                        // loop until all queued frames processed
                {
                    Sample f = _frameQueue.Dequeue();
                    Bitmap b = (Bitmap)f._objects["Bitmap 8BppMono"];

                    Byte threshold = (Byte)StateStore.Store["BlobThreshold"];
                    Blob[] blobs = FindBlobs(b, threshold);
                    f._objects.Add("Blob Array", blobs);
                }                                                                    // end of while (_frameQueue.Count > 0)
            }                                                                        // end of while (KeepGoing(100))
        }

        #endregion

        public static Blob[] FindBlobs(Bitmap inputImage, Byte threshold)
        {
            Byte[,] blobArray = new Byte[inputImage.Height, inputImage.Width];
            Byte currentBlob = 1;
            int Width = inputImage.Width;
            int Height = inputImage.Height;

            List<Byte> badBlobs = new List<byte>();

            unsafe
            {
                BitmapData bd = inputImage.LockBits(
                    new Rectangle(0, 0, inputImage.Width, inputImage.Height),
                    ImageLockMode.ReadOnly,
                    PixelFormat.Format8bppIndexed);

                Byte * firstPixel = (Byte*)bd.Scan0;
                Byte* lastPixel = firstPixel + inputImage.Width * inputImage.Height;

                fixed (Byte* blobStart = blobArray)
                {
                    // set the edges of the blob array to 255

                    // start with the top and bottom lines
                    Byte* blobEnd = blobStart + Width * Height;         // one byte passed the end
                    Byte* blobIndex = blobStart;
                    Byte* blobIndex2 = blobEnd - Width;                 // first pixel of the last line
                    Byte* blobEndOfLine = blobStart + Width;
                    while (blobIndex < blobEndOfLine)
                    {
                        *blobIndex++ = 255;
                        *blobIndex2++ = 255;
                    }

                    // and now set the left and right edges to 255
                    blobIndex = blobStart;
                    blobIndex2 = blobStart + Width - 1;                 // last pixel in the first line
                    while (blobIndex < blobEnd)
                    {
                        *blobIndex = 255;
                        blobIndex += Width;
                        *blobIndex2 = 255;
                        blobIndex2 += Width;
                    }

                    // walk through the pixels
                    Byte* pixel = firstPixel;
                    blobIndex = blobStart;
                    while (pixel < lastPixel)
                    {
                        if ((*pixel > threshold) && (*blobIndex == 0))
                        {
                            if (*(blobIndex - 1) != 0)                          // continuing blob from previous pixel
                            {
                                *blobIndex = *(blobIndex - 1);
                            }
                            else                                                // see if attached to blob on previous line
                            {
                                Byte continuingBlob = 0;
                                Byte* i = pixel;
                                Byte* b = blobIndex - Width;
                                bool done = false;
                                while (!done)
                                {
                                    if (*b != 0)                                // attached to blob above -- continue it
                                    {
                                        continuingBlob = *b;
                                        done = true;
                                    }
                                    ++i;
                                    ++b;
                                    if (*i < threshold)                       // end of blob on this line
                                    {
                                        done = true;
                                    }
                                }
                                if (continuingBlob == 0)                        // beginning of a new blob
                                {
                                    *blobIndex = currentBlob++;
                                    if (currentBlob == 255)                     // ERROR: too many blobs
                                        pixel = lastPixel;                      //TODO: report error
                                }
                                else                                            // continuation of previous blob
                                {
                                    *blobIndex = continuingBlob;
                                }
                            }

                                
                        }
                        ++pixel;
                        ++blobIndex;
                    }
                    // get rid of any blobs that are touching the edges


                    // start with the top and bottom lines
                    blobEnd = blobStart + Width * Height;
                    blobIndex = blobStart + Width;
                    blobIndex2 = blobEnd - 2 * Width;                 // last line
                    blobEndOfLine = blobIndex + Width;
                    while (blobIndex < blobEndOfLine)
                    {
                        if ((*blobIndex != 255) && (*blobIndex != 0) && !(badBlobs.Contains(*blobIndex)))
                            badBlobs.Add(*blobIndex);
                        if ((*blobIndex2 != 255) && (*blobIndex2 != 0) && !(badBlobs.Contains(*blobIndex2)))
                            badBlobs.Add(*blobIndex2);
                        ++blobIndex;
                        ++blobIndex2;
                    }
                    blobIndex = blobStart + 1;
                    blobIndex2 = blobStart + Width - 2;                 // last pixel in the first line
                    while (blobIndex < blobEnd)
                    {
                        if ((*blobIndex != 255) && (*blobIndex != 0) && !(badBlobs.Contains(*blobIndex)))
                            badBlobs.Add(*blobIndex);
                        if ((*blobIndex2 != 255) && (*blobIndex2 != 0) && !(badBlobs.Contains(*blobIndex2)))
                            badBlobs.Add(*blobIndex2);
                        blobIndex += Width;
                        blobIndex2 += Width;
                    }

                }
            }

            // make an array of all the blobs
            int x, y;
            Dictionary<Byte, Blob> o = new Dictionary<Byte, Blob>();
 
            for (y = 0; y < Height; ++y)
            {
                int startIndex = blobArray[y, 1];
                if (startIndex == 255) startIndex = 0;
                for (x = 0; x < Width; ++x)
                {
                    Byte bv = blobArray[y, x];
                    if ((bv > 0) && (bv < 255) && (startIndex == 0))
                    {
                        startIndex = x;
                    }
                    if ((bv == 0) && (startIndex != 0))
                    {
                        if (!badBlobs.Contains(blobArray[y, x - 1]))
                        {
                            Blob b;

                            if (o.ContainsKey(blobArray[y, x - 1]))
                                b = o[blobArray[y, x - 1]];
                            else
                            {
                                b = new Blob();
                                o.Add(blobArray[y, x - 1], b);
                            }
                            b.AddSpan(new Rectangle(startIndex, y, x - startIndex, 1));
                        }
                        else
                        {
                        }
                        startIndex = 0;
                    }
                }
            }
            Blob[] oa = new Blob[o.Count];
            int ii = 0;
            foreach (Blob b in o.Values)
            {
                oa[ii++] = b;
            }
            return oa;
        }

    }
}
